using System;
using System.Collections.Generic;
using System.Text;
using System.CodeDom;
using System.IO;
using System.CodeDom.Compiler;
using OpenArrow.Templating.Parsing;
using OpenArrow.Templating.Compilation;

namespace OpenArrow.Templating.Processing
{
    public class StandardTemplateProcessor : ITemplateProcessor
    {
        private ITemplateParser parser = null;
        private ITemplateCompiler compiler = null;

        public ITemplateParser Parser
        {
            get
            {
                if (parser == null)
                    parser = new ASPNetStyleTemplateParser();
                return parser;
            }
            set { parser = value; }
        }

        public ITemplateCompiler Compiler
        {
            get
            {
                if (compiler == null)
                    compiler = new StandardTemplateCompiler();
                return compiler;
            }
            set { compiler = value; }
        }

        #region ITemplateProcessor Members

        public void GenerateSource(TextReader template, TextWriter output, CodeGeneratorOptions generatorOptions)
        {
            ProblemCollection problems = new ProblemCollection();
            GenerateSource(template, output, generatorOptions, problems);
            problems.Throw();
        }

        public bool GenerateSource(TextReader template, TextWriter output, CodeGeneratorOptions generatorOptions, ProblemCollection processorErrors)
        {
            InternalGenerateSource(template, output, generatorOptions, processorErrors);
            
            // Check for errors
            return processorErrors.ErrorCount == 0;
        }

        public CodeCompileUnit GenerateSourceTree(TextReader template)
        {
            ProblemCollection problems = new ProblemCollection();
            CodeCompileUnit ret = GenerateSourceTree(template, problems);
            problems.Throw();
            return ret;
        }

        public CodeCompileUnit GenerateSourceTree(TextReader template, ProblemCollection processorErrors)
        {
            ParsedTemplate parsedTemplate;
            return InternalGenerateSourceTree(template, processorErrors, out parsedTemplate);
        }

        public CompilerResults CompileAssembly(TextReader template, CompilerParameters parameters, string[] extraFiles, 
            string[] assemblySearchPaths)
        {
            ProblemCollection problems = new ProblemCollection();
            CompilerResults ret = CompileAssembly(template, parameters, extraFiles, problems, assemblySearchPaths);
            problems.Throw();
            return ret;
        }

        public CompilerResults CompileAssembly(TextReader template, CompilerParameters parameters, string[] extraFiles,
            ProblemCollection processorErrors, string[] assemblySearchPaths)
        {
            // Open a temporary file for the template source
            string tempPath = Path.GetTempFileName();
            TextWriter writer = File.CreateText(tempPath);

            // Generate the template source
            CodeDomProvider provider = InternalGenerateSource(template, writer, new CodeGeneratorOptions(), processorErrors);
            string newTempPath = String.Format("{0}.{1}", tempPath, provider.FileExtension);
            File.Move(tempPath, newTempPath);



            // Compile the template and extra files
            string[] fileNames = new string[extraFiles.Length + 1];
            fileNames[0] = newTempPath;
            Array.Copy(extraFiles, 0, fileNames, 1, extraFiles.Length);
            CompilerResults results = provider.CompileAssemblyFromFile(parameters, fileNames);

            // Delete the temporary file
            File.Delete(newTempPath);

            // Return the results
            return results;
        }

        public ITemplateParser GetParser()
        {
            return Parser;
        }

        public ITemplateCompiler GetCompiler()
        {
            return Compiler;
        }

        #endregion

        #region Private Methods

        private CodeCompileUnit InternalGenerateSourceTree(TextReader template, ProblemCollection processorErrors, out ParsedTemplate parsedTemplate)
        {
            // Phase 1 - Parse the template
            parsedTemplate = Parser.Parse(template, processorErrors);

            // Phase 2 - Compile the template to source code
            return Compiler.GenerateSource(parsedTemplate, processorErrors);
        }

        private CodeDomProvider InternalGenerateSource(TextReader template, TextWriter output, CodeGeneratorOptions generatorOptions, ProblemCollection processorErrors)
        {
            // Get the Source Tree
            ParsedTemplate parsedTemplate;
            CodeCompileUnit sourceTree = InternalGenerateSourceTree(template, processorErrors, out parsedTemplate);

            // Get the CodeDomProvider
            CodeDomProvider provider = CodeDomProvider.CreateProvider(parsedTemplate.TemplateDirective.Language);

            // Generate the Code
            provider.GenerateCodeFromCompileUnit(sourceTree, output, generatorOptions);
            return provider;
        }

        #endregion
    }
}
